import fs from "fs";
import {
    workerData,
    parentPort
} from "worker_threads";
import {
    Tokenizer
} from "./lexer/lexer.node.js";
import * as Ast from "./ast/ast.node.js";
import {
    Transformer
} from "./transform/transformer.node.js";
import {
    CodeGenerator
} from "./codegen/codegen.node.js";
import {
    EOF,
    NewLine
} from "./token/tokenKind.node.js";
import {
    Parser
} from "./parser/parser.node.js";

function makeIndex(tokens) {
    // for each the token, we needed to transform index to line column.
    let line = 1;
    let column = 0;
    let index = 0;
    while (index < tokens.length) {
        let token = tokens[index];
        let startLine = line;
        let startColumn = column;
        if (token.tokenKind === NewLine) {
            line += token.end - token.start;
            column = 0;
            // set the position.
            token.startLine = startLine;
            token.startColumn = startColumn;
            token.endLine = line;
            token.endColumn = column;
        } else {
            let value = token.value;
            let valueIndex = 0;
            while (valueIndex < value.length) {
                let ch = value[valueIndex];
                if (ch == '\r' || ch == '\n') {
                    ++line;
                    column = 0;
                } else {
                    ++column;
                }
                ++valueIndex;
            }
            token.startLine = startLine;
            token.startColumn = startColumn;
            token.endLine = line;
            token.endColumn = column;
        }
        ++index;
    }
}

function tokenizeAll(str) {
    const tokenizer = new Tokenizer(str);
    const tokens = [];
    let token = tokenizer.nextToken();
    while (token.tokenKind !== EOF) {
        tokens.push(token);
        token = tokenizer.nextToken();
    }

    // make index for tokens
    makeIndex(tokens, str);

    // return the tokens
    return tokens;
}

function codegen(
    ast,
    format = false,
    computedMode = "default",
    bracketForExpressionStatement = false
) {
    const codeGenerator = new CodeGenerator(ast);
    codeGenerator.setFormat(format);
    codeGenerator.setComputedMode(computedMode);
    codeGenerator.setBracketForExpressionStatement(bracketForExpressionStatement);
    return codeGenerator.generate();
}

function generateAST(tokens) {
    const parser = new Parser(tokens);
    parser.parseProgram();
    return parser.ast;
}

const sourcePath = workerData.sourcePath;
const astPath = workerData.astPath;
const targetPath = workerData.targetPath;

function main() {
    let source = null;
    let tokens = null;
    let ast = null;
    let code = null;

    try {
        source = fs.readFileSync(sourcePath).toString();
    } catch (err) {
        parentPort.postMessage({
            error: "读取源文件错误: " + err,
            dismiss: true
        });
        return;
    }

    parentPort.postMessage({
        text: "正在进行词法分析...."
    });

    try {
        tokens = tokenizeAll(source);
    } catch (err) {
        parentPort.postMessage({
            error: "词法分析错误: " + err,
            dismiss: true
        });
        return;
    }

    parentPort.postMessage({
        text: "正在进行语法分析...."
    });

    try {
        ast = generateAST(tokens);
    } catch (err) {
        parentPort.postMessage({
            error: "语法分析错误: " + err,
            dismiss: true
        });
        console.error(err)
        return;
    }

    if (workerData.generateASTJsonChecked) {
        fs.writeFile(astPath, JSON.stringify(ast, null, 4), function() {});
    }
    
    parentPort.postMessage({
        text: "正在进行代码生成...."
    });

    try {
        code = codegen(ast, workerData.formatChecked, workerData.computedMode, workerData.exprContainerChecked);
    } catch (err) {
        parentPort.postMessage({
            error: "代码生成器错误: " + err,
            dismiss: true
        });
        return;
    }

    parentPort.postMessage({
        text: "正在写入文件...."
    });

    try {
        fs.writeFileSync(workerData.targetPath, code);
    } catch (err) {
        parentPort.postMessage({
            error: "写入文件错误: " + err,
            dismiss: true
        });
        return;
    }

    parentPort.postMessage({
        success: true,
        dismiss: true
    });
}

main();